package cn.learn.designpattern.strategy;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

/**
 * 扫描注解,根据注解生成对应的策略对象（对象生成工厂）
 * @author huangyezhan
 * @date 2018/11/20-23:27
 */
public class SalePriceFactory {

    private SalePriceFactory() {
        init();
    }

    /**
     * 单例对象
     */
    public static SalePriceFactory getInstance() {
        return SalePriceFactoryInstance.instance;
    }

    /**
     * 单例静态内部类,效率高,线程安全
     * 类级内部类，就是静态内部类，这种内部类与其外部类之间并没有从属关系，加载外部类的时候，
     * 并不会同时加载其静态内部类，只有在发生调用的时候才会进行加载，
     * 加载的时候就会创建单例实例并返回，有效实现了懒加载（延迟加载）
     */
    private static class SalePriceFactoryInstance {
        private static SalePriceFactory instance = new SalePriceFactory();
    }


    /**
     * 这里是一个常量，表示我们扫描策略的包
     */
    private static final String SALE_PRICE_PACKAGE = SalePriceFactory.class.getPackage().getName() + ".algorithm";

    /**
     * 策略列表
     */
    private List<Class<? extends SalePrice>> salePriceList;

    private ClassLoader classLoader = getClass().getClassLoader();


    /**
     * 根据客户的总金额产生相应的策略对象
     * @param totalAmount
     * @return
     */
    public SalePrice createSalePrice(BigDecimal totalAmount) {
        //在策略列表查找策略
        for (Class<? extends SalePrice> clazz : salePriceList) {
            //获取该策略的注解
            PriceRegion validRegion = this.handleAnnotation(clazz);
            //判断金额是否在注解的区间
            //if (totalAmount > validRegion.min() && totalAmount < validRegion.max()) {
            if (totalAmount.compareTo(BigDecimal.valueOf(validRegion.min())) > 0 && totalAmount.compareTo(BigDecimal.valueOf(validRegion.max())) <= 0) {
                try {
                    //是的话我们返回一个当前策略的实例
                    return clazz.newInstance();
                } catch (Exception e) {
                    throw new RuntimeException("策略获得失败");
                }
            }
        }
        throw new RuntimeException("策略获得失败");
    }

    /**
     * 处理注解，我们传入一个策略类，返回它的注解
     * @param clazz
     * @return
     */
    private PriceRegion handleAnnotation(Class<? extends SalePrice> clazz) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        if (annotations == null || annotations.length == 0) {
            return null;
        }
        for (int i = 0; i < annotations.length; i++) {
            if (annotations[i] instanceof PriceRegion) {
                return (PriceRegion) annotations[i];
            }
        }
        return null;
    }

    /**
     * 在工厂初始化时要初始化策略列表
     */
    private void init() {
        salePriceList = new ArrayList<Class<? extends SalePrice>>();
        File[] resources = this.getResources();
        Class<SalePrice> salePriceClass = null;
        try {
            //使用相同的加载器加载策略接口
            salePriceClass = (Class<SalePrice>) classLoader.loadClass(SalePrice.class.getName());
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException("未找到策略接口");
        }

        try {
            for (int i = 0; i < resources.length; i++) {
                //载入包下的类
                Class<?> clazz = classLoader.loadClass(SALE_PRICE_PACKAGE + "." + resources[i].getName().replace(".class", ""));
                //判断是否是SalePrice的实现类并且不是SalePrice它本身，满足的话加入到策略列表
                if (SalePrice.class.isAssignableFrom(clazz) && clazz != salePriceClass) {
                    salePriceList.add((Class<? extends SalePrice>) clazz);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取扫描的指定包下面所有的class文件
     * @return  返回所有的class文件
     */
    private File[] getResources() {
        try {
            File file = new File(classLoader.getResource(SALE_PRICE_PACKAGE.replace(".", "/")).toURI());
            return file.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    //只扫描class文件
                    if (pathname.getName().endsWith(".class")) {
                        return true;
                    }
                    return false;
                }
            });
        } catch (URISyntaxException e) {
            throw new RuntimeException("未找到策略资源");
        }
    }



}
